package com.xishu.service;

import com.xishu.bo.Stock;
import com.xishu.config.Config;
import com.xishu.entity.order.Order;
import com.xishu.entity.order.OrderItem;
import com.xishu.entity.shop.*;
import com.xishu.exception.ResponseStatusException;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.service.order.base.OrderService;
import com.xishu.util.BeanUtil;
import com.xishu.util.ClassUtil;
import com.xishu.util.ServletUtil;
import com.xishu.util.Tools;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.*;
import java.util.stream.Collectors;

import static com.xishu.config.Constant.SPECIAL_ITEM;

public class StockService implements Tools {
    private static Logger logger = LoggerFactory.getLogger(StockService.class);

    private static StockService instance = new StockService();
    private CommonService commonService = CommonService.getInstance();
    private ItemService itemService = ItemService.getInstance();
    private LockService localLockService = LockService.getInstance();
    private Config config = Config.getInstance();


    private StockService() {
    }

    public static StockService getInstance() {
        return instance;
    }


    /**
     * 库存管理的锁ID
     *
     * @param shopId
     * @return
     */
    public String getStockKey(Long shopId) {
        return "/stock/" + shopId;
    }

    /**
     * 检查锁定量是否足够
     * <p>
     * 检查的场景是因为锁定量足够的情况下，有可能服务员会去修改余量
     * 这种场景下不用加锁，直接判断余量是否足够
     * 本身这种场景出现的较小，加锁虽然会提高一致性，但是性能会降低
     *
     * @param orderItemIdList 菜品ID列表
     * @return
     */
    public List<Stock> checkLockStockAvailable(List<Long> orderItemIdList, Order order) throws Exception {
        if (isEmpty(orderItemIdList)) {
            logger.info("orderItemIdList is null");
            return null;
        }

        if (order == null) {
            logger.info("order is null");
            return null;
        }

        OrderService orderService = OrderService.getInstance();

        List<OrderItem> orderItemList = orderService.filterOrderItemList(order, orderItemIdList);

        return checkOrderItemListStock(orderItemList);
    }

    /**
     * 将分店里面的锁定量转化成余量
     * <p>
     * 如果一开始锁定量是够的，后面服务员把余量修改成不足了，怎么处理，这里会有不同的结果
     * <p>
     * ok
     *
     * @param orderInDb
     * @param orderItemList
     */
    public void lockCountToPrinterCount(Order orderInDb, List<OrderItem> orderItemList) throws Exception {
        if (orderInDb == null) {
            logger.info("shopId is null");
            VerifyUtil.throwError(ResponseStatus.INVALID_PARAMETER);
            return;
        }

        if (isEmpty(orderItemList)) {
            logger.info("order item is null");
            return;
        }

        orderItemList = orderItemList.stream().filter(p -> getLong(p.getId()) != 0 || getLong(p.getItemId()) == 0).collect(Collectors.toList());

        for (OrderItem orderItem : orderItemList) {
            //特殊菜不处理库存
            if (equals(orderItem.getItemId(), SPECIAL_ITEM)) {
                continue;
            }

            //不加配也不处理
            if (getLong(orderItem.getItemId()) == 0l) {
                continue;
            }

            OrderItemLockNumber orderItemLockNumber = filterOrderItemLockNumber(orderInDb, orderItem.getId());

            for (ItemLockNumber itemLockNumber : orderItemLockNumber.getItemLockNumberList()) {
                Item item = itemService.findItem(itemLockNumber.getItemId());
                Long itemId = item.getId();

                if (item.getLeftCount() != null) {
                    //减少余量
                    Integer leftCount = item.getLeftCount();

                    if (leftCount != null) {
                        leftCount = leftCount - itemLockNumber.getLockCount();

                        if (leftCount < 0) {
                            logger.info("item {} left count to 0", item.getId());
                            leftCount = 0;
                        }

                        logger.info("item {} set left count to {}", item.getId(), leftCount);
                        item.setLeftCount(leftCount);

                        if (leftCount == 0) {
                            item.setSaleOut(true);
                            //同步修改映射关系
                            MappingService.getInstance().modifyLeftCount(item.getId(), 0);
                            //将相关的套餐也设置成售罄，如果可以的话
                            ItemService.getInstance().setSetMenuSaleOut(item);
                        }

                        //同步减少映射里面的余量
                        MappingService.getInstance().modifyLeftCount(itemId, leftCount);

                        cutItemLockCount(itemLockNumber, item);

                        commonService.save(item);
                    }
                } else {
                    //没有设置余量，那么直接减少锁定量
                    cutItemLockCount(itemLockNumber, item);
                    commonService.save(item);
                }
            }
        }
    }

    /**
     * 发送厨房之的之后，减少菜品的锁定量
     *
     * @param itemLockNumber
     * @param item
     */
    private void cutItemLockCount(ItemLockNumber itemLockNumber, Item item) {
        //减少锁定量
        int lockCount = getInt(item.getLockCount());
        lockCount = lockCount - itemLockNumber.getLockCount();

        if (lockCount < 0) {
            logger.info("item {} lock count set to 0", item.getId());
            lockCount = 0;
        }

        logger.info("item {} set lock count to {}", item.getId(), lockCount);
        item.setLockCount(lockCount);
    }

    /**
     * 库存管理，加锁操作
     * 只处理菜的库存，菜在每个订单上面的锁定量，由外层处理
     * <p>
     * ok
     *
     * <p>
     * 库存分为余量和锁定量以及当前可用量
     * <p>
     * 如果已经售罄了，那么直接提示已售罄，不可用
     * 如果没有超过当前的可用量，那么直接可用
     * 如果当前是做余量处理，那么看余量是否有超过可用量，没有超过，那么可用
     *
     * @param shopId    分店ID
     * @param stockList 需要处理的库存
     */
    public List<Stock> stockManage(Long shopId, List<Stock> stockList) throws Exception {
        if (shopId == null) {
            logger.info("shop id is null");
            VerifyUtil.throwError(ResponseStatus.INVALID_PARAMETER);
            return new ArrayList<>();
        }

        stockList = getList(stockList).stream().filter(p -> p.getItemId() != null && p.getItemId() != 0).collect(Collectors.toList());

        if (isEmpty(stockList)) {
            logger.info("stock list is empty");
            return new ArrayList<>();
        }

        List<Long> itemIdList = stockList.stream().map(p -> p.getItemId()).collect(Collectors.toList());
        List<Item> itemList = ItemService.getInstance().findItemByItemList(itemIdList);

        for (Stock stock : stockList) {
            //第一次处理库存，如果都满足，后面肯定不用再次扫描库存
            firstScanItemStock(itemList, stock);
        }

        //检查是否有超过可用量的菜品，如果超过了，那么需要扫描订单
        Optional<Stock> orderScanOptional = stockList.stream().filter(p -> getBoolean(p.getScanOrder())).findAny();

        if (orderScanOptional.isPresent()) {
            logger.info("will scan order");
            //如果上次扫描的时间距离当前没有超过N秒，那么不扫描,压力太大时，把时间调整的更长
            Shop shop = ShopService.getInstance().findShopById(shopId);
            long currentTimeMillis = System.currentTimeMillis();
            if (currentTimeMillis - getLong(shop.getLastScanTime()) > config.scanOrderInterval()) {
                scanShopTimeExpiredOrder(shop);
            }

            //重新计算过库存之后，再计算当前菜品是否足够下单
            //没有扫描过，那么直接用现有的库存来计算，提示客户当前可用的余量是多少
            calStockAvailableCount(stockList);
            formatAvailableCount(stockList);
        }

        boolean allOk = stockList.stream().allMatch(p -> !getBoolean(p.getError()));

        //如果所有的订单都是OK的，那么才去扣除余量或者是锁定量
        if (allOk) {
            logger.info("all stock is ok");
            cutLockNumber(stockList);
            return new ArrayList<>();
        } else {
            //存在菜品不足，那么需要给客户端提示
            logger.info("some stock has error");
            stockList = stockList.stream().filter(p -> getBoolean(p.getError())).collect(Collectors.toList());

            //如果小于0了，那么显示了
            for (Stock stock : stockList) {
                if (getInt(stock.getAvailableCount()) < 0) {
                    stock.setAvailableCount(0);
                }
            }

            return stockList;
        }
    }

    /**
     * 计算每个菜的可用库存
     * <p>
     * ok
     *
     * @param stockList
     */
    private void calStockAvailableCount(List<Stock> stockList) {
        //过滤出有扫描的菜品，然后计算出还有多少菜品数量可以下单的
        List<Stock> scanStock = stockList.stream().filter(p -> getBoolean(p.getScanOrder())).collect(Collectors.toList());
        if (isNotEmpty(scanStock)) {
            List<Long> scanStockItemIdList = scanStock.stream().map(p -> p.getItemId()).collect(Collectors.toList());
            List<Item> scanStockItemList = itemService.findItemByItemList(scanStockItemIdList);

            logger.info("will cal stock available count");
            for (Stock stock : scanStock) {
                //如果没有超过可用数据，那么直接可用
                Optional<Item> itemOptional = itemService.filterItem(scanStockItemList, stock.getItemId());
                if (itemOptional.isPresent()) {
                    Item item = itemOptional.get();
                    itemService.calItemAvailableItem(item);
                    itemService.calItemCutNumber(stock);

                    //如果依然不可用，那么添加报错信息
                    if (item.getAvailableCount() >= stock.getCutItemCount()) {
                        logger.info("after scan order list, lock count ok {}", item.getId());
                        continue;
                    }

                    //如果还是不可用，那么设置error，并且设置可用的数量
                    stock.setError(true);
                    stock.setAvailableCount(item.getAvailableCount());
                } else {
                    logger.info("can not find the item {}", stock.getItemId());
                }
            }
        }
    }

    /**
     * 第一次处理库存，如果都满足，后面肯定不用再次扫描库存
     * <p>
     * ok
     *
     * @param itemList
     * @param stock
     * @return
     */
    private void firstScanItemStock(List<Item> itemList, Stock stock) throws ResponseStatusException {
        ItemService itemService = ItemService.getInstance();
        Optional<Item> itemOptional = ItemService.getInstance().filterItem(itemList, stock.getItemId());
        logger.info("first scan for item {}", stock.getItemId());
        if (!itemOptional.isPresent()) {
            logger.info("can not find the item {}", stock.getItemId());
            VerifyUtil.throwError(ResponseStatus.SYSTEM_ERROR);
            return;
        } else {
            Item item = itemOptional.get();
            stock.setItem(item);

            //如果存在一个菜品，如果已经售罄了，那么提示售罄了
            if (getBoolean(item.getSaleOut())) {
                logger.info("item {} sale out ", item.getId());
                stock.setSaleOut(true);
                stock.setError(true);
                return;
            }

            //如果没有设置余量
            if (item.getLeftCount() == null) {
                logger.info("item {} not set left count", item.getId());
                return;
            }

            //如果没有超过可用数据，那么直接可用
            itemService.calItemAvailableItem(item);
            itemService.calItemCutNumber(stock);
            if (getInt(stock.getCutItemCount()) <= item.getAvailableCount()) {
                logger.info("item cut item count is {}, available count {}", stock.getCutItemCount(), item.getAvailableCount());
                return;
            }

            //如果不够了，但是没有锁定量，那么也不用扫描，直接给出剩下量
            if (getInt(item.getLockCount()) == 0) {
                logger.info("no item lock count, direct sale out");
                stock.setAvailableCount(item.getLeftCount());
                stock.setError(true);
                return;
            }

            //那么这里一定是超了,也就是需要扫描订单，来决定是否有订单的锁定量可以释放的
            logger.info("item need to scan order");
            stock.setScanOrder(true);
            return;
        }
    }

    /**
     * 扫描过期的订单
     * 决定是否有订单可以释放量的，同时记录本次的扫描时间，以避免在相同的时间内重复扫描
     * <p>
     * ok
     *
     * @param shop
     * @throws Exception
     */
    public void scanShopTimeExpiredOrder(Shop shop) throws Exception {
        OrderService orderService = OrderService.getInstance();
        long currentTimeMillis = System.currentTimeMillis();
        Long shopId = shop.getId();
        logger.info("update the shop {} last scan time {}", shopId, currentTimeMillis);
        shop.setLastScanTime(System.currentTimeMillis());
        commonService.save(shop);

        //开始扫描订单,
        //需要扫描锁定的订单，看是否有需要释放的订单
        //这里直接过滤出来的是已过期的订单
        //所有的C端有锁定量的订单，并且没有超时的订单，再过滤一遍，看有没有超时
        List<Order> lockOrderList = orderService.findLockOrderList(shopId);

        logger.info("expire time order size is {}", lockOrderList.size());

        //已经锁定的订单，那么扫描，并释放掉
        for (Order order : lockOrderList) {
            releaseLockNumber(order);
            //将订单修改成已过期,无论如何都算过期了
            order.setTimeExpiredOrder(true);
            commonService.save(order);
        }
    }

    /**
     * 释放菜的锁定量
     *
     * @param orderInDb
     * @param orderItemId
     */
    public void releaseLockNumber(Order orderInDb, Long orderItemId) throws Exception {
        OrderItemLockNumber orderItemLockNumber = filterOrderItemLockNumber(orderInDb, orderItemId);
        for (ItemLockNumber itemLockNumber : orderItemLockNumber.getItemLockNumberList()) {
            releaseLockNumber(itemLockNumber);
        }
    }

    /**
     * 释放OrderItem锁定量
     *
     * @param orderItemLockNumber
     * @throws Exception
     */
    public void releaseLockNumber(OrderItemLockNumber orderItemLockNumber) throws Exception {
        for (ItemLockNumber itemLockNumber : orderItemLockNumber.getItemLockNumberList()) {
            releaseLockNumber(itemLockNumber);
        }
    }

    /**
     * 释放批量的锁定量
     *
     * @param orderInDb
     * @param orderItemIdList
     * @throws Exception
     */
    public void releaseLockNumber(Order orderInDb, List<Long> orderItemIdList) throws Exception {
        for (Long orderItemId : getList(orderItemIdList)) {
            releaseLockNumber(orderInDb, orderItemId);
        }
    }

    /**
     * 释放整个订单的锁定量
     *
     * @param orderInDb
     */
    public void releaseLockNumber(Order orderInDb) throws Exception {
        List<OrderItemLockNumber> itemLockNumberList = orderInDb.getItemLockNumberList();

        //释放掉菜品锁定量,释放数组里面的菜
        for (OrderItemLockNumber orderItemLockNumber : itemLockNumberList) {
            orderItemLockNumber.getItemLockNumberList().forEach(itemLockNumber -> {
                try {
                    releaseLockNumber(itemLockNumber);
                } catch (Exception e) {
                    logger.error("e", e);
                }
            });
        }
    }

    /**
     * 释放菜品锁定量
     * ok
     *
     * @param itemLockNumber
     */
    public void releaseLockNumber(ItemLockNumber itemLockNumber) throws Exception {
        if (!getBoolean(itemLockNumber.getRealLock())) {
            logger.info("do not real lock item {}, return ", itemLockNumber.getItemId());
            return;
        }

        //已经打印过了，不再重复释放锁定量
        if (getInt(itemLockNumber.getPrinterCount()) > 0) {
            logger.info("{} do not release the lock number again", itemLockNumber.getItemId());
            return;
        }

        Long itemId = itemLockNumber.getItemId();
        Item item = ItemService.getInstance().findItem(itemId);

        Integer lockCount = itemLockNumber.getLockCount();
        //释放锁定量
        if (lockCount != null && lockCount >= 0) {
            logger.info("will release lock count {}", lockCount);
            int itemLockCount = getInt(item.getLockCount());
            itemLockCount = itemLockCount - lockCount;

            if (itemLockCount < 0) {
                logger.info("may be some error, lock count less then 0");
                itemLockCount = 0;
            }

            item.setLockCount(itemLockCount);
        }

        commonService.save(item);
    }

    /**
     * 开始扣除库存余量
     * ok
     *
     * @param stockList
     */
    public void cutLockNumber(List<Stock> stockList) throws Exception {
        if (isEmpty(stockList)) {
            logger.info("stock list is empty");
            return;
        }

        List<Long> itemIdList = stockList.stream().map(p -> p.getItemId()).collect(Collectors.toList());
        ItemService itemService = ItemService.getInstance();
        List<Item> itemList = itemService.findItemByItemList(itemIdList);
        for (Stock stock : stockList) {
            Optional<Item> itemOptional = itemService.filterItem(itemList, stock.getItemId());
            if (!itemOptional.isPresent()) {
                logger.info("can not find the item, {}", stock.getItemId());
                continue;
            }

            Item item = itemOptional.get();
            //开始减锁定量或者是余量
            if (stock.getLeftCount() != null && item.getLeftCount() != null) {
                int leftCount = item.getLeftCount();
                leftCount = leftCount - stock.getLeftCount();
                if (leftCount < 0) {
                    logger.info("may be some error");
                    leftCount = 0;
                }

                logger.info("item {} left count is {}", stock.getItemId(), leftCount);
                item.setLeftCount(leftCount);

                //同步修改映射关系
                MappingService.getInstance().modifyLeftCount(stock.getItemId(), leftCount);
            }

            //开始增加锁定量
            if (stock.getLockCount() != null) {
                int lockCount = getInt(item.getLockCount());
                lockCount = lockCount + stock.getLockCount();
                logger.info("item {} lock count {}", stock.getItemId(), lockCount);
                item.setLockCount(lockCount);
            }

            //如果减了之后，是售罄了，那么修改成售罄
            if (item.getLeftCount() != null && getInt(item.getLeftCount()) <= 0) {
                item.setSaleOut(true);
                //同步修改映射关系
                MappingService.getInstance().modifyLeftCount(item.getId(), 0);
                //同时将套餐设置成售罄
                ItemService.getInstance().setSetMenuSaleOut(item);
            }

            commonService.save(item);
        }
    }

    /**
     * 减少一个数量时，对应菜品的锁定量,只计算出被减少的每个菜的锁定量，并不对订单做实际的修改
     *
     * @param orderInDb
     * @param orderItemId
     * @return
     */
//    public List<OrderItemLockNumber> cutOneOrderItemLockNumber(Order orderInDb, Long orderItemId) throws Exception {
//        Order order = (Order) ClassUtil.clone(orderInDb);
//
//        OrderService orderService = OrderService.getInstance();
//        OrderItem orderItem = orderService.filterOrderItemList(order, orderItemId);
//        List<ItemLockNumber> lockNumberList = orderItemToLockNumberList(orderItem);
//        //减少一个数量之后的锁定量
//        orderItem.setCount(orderItem.getCount() - 1);
//        List<OrderItemLockNumber> afterCutItemLockNumberList = orderItemToLockNumberList(orderItem);
//
//        //依次计算减少的锁定量
//        for (OrderItemLockNumber itemLockNumber : lockNumberList) {
//            Optional<OrderItemLockNumber> itemLockNumberOptional = afterCutItemLockNumberList.stream().filter(p -> equals(p.getItemId(), itemLockNumber.getItemId())).findAny();
//            if (itemLockNumberOptional.isPresent()) {
//                OrderItemLockNumber itemLockNumberInFilter = itemLockNumberOptional.get();
//                itemLockNumber.setLockCount(itemLockNumber.getLockCount() - itemLockNumberInFilter.getLockCount());
//            } else {
//                logger.info("can not find the item {} lock number", itemLockNumber.getItemId());
//            }
//        }
//
//        return lockNumberList;
//    }

    /**
     * B端更新库存
     * ok
     *
     * @param orderInDb
     * @param increaseNumber   是增加操作还是减少操作
     * @param countAfterUpdate 更新后的数量
     * @param orderItem
     * @throws Exception
     */

    public List<Stock> waiterUpdateOrderItemLock(Order orderInDb, OrderItem orderItem, boolean increaseNumber, int countAfterUpdate) throws Exception {
        if (!config.stockManageOpen()) {
            logger.info("do not open the stock manage");
            return new ArrayList<>();
        }

        //C端更新菜品的数量，不用处理锁定量
        Long userId = ServletUtil.getUserId();
        if (userId == null) {
            logger.info("c end update order item count");
            return new ArrayList<>();
        }

        Long itemId = orderItem.getItemId();
        //特别菜不用处理库存的问题
        if (getLong(itemId) == SPECIAL_ITEM) {
            return new ArrayList<>();
        }

        Item item = ItemService.getInstance().findItem(itemId);

        //这里其实应该判断套餐里面的每一菜是否都没有设置余量，当前由于时间的关系，只处理当前套餐的余量
        if (item.getLeftCount() == null && !getBoolean(item.getSaleOut())) {
            logger.info("do not set the left count");
            //将菜的锁定量直接更新
            orderItem = (OrderItem) ClassUtil.clone(orderItem);
            orderItem.setCount(countAfterUpdate);
            updateOrderItemLockNumber(orderInDb, orderItem, false);
            return new ArrayList<>();
        }

        List<Item> itemList = OrderService.getInstance().orderItemToItemList(orderItem);
        List<Stock> saleOutStockList = toSaleOutStockList(itemList);

        //套餐里面任何一个菜售罄了
        if (isNotEmpty(saleOutStockList)) {
            return saleOutStockList;
        }

        //查看锁定量里面，是否有该ID，如果没有，则直接不用判断，退出
        logger.info("item is {} order Item in db {}, req order Item {}", orderItem.getId(), orderItem.getCount(), increaseNumber);

        //库存判断，加锁操作
        String lockKey = StockService.getInstance().getStockKey(orderInDb.getShopId());
        Object lock = localLockService.getLock(lockKey);

        Long orderItemId = orderItem.getId();

        synchronized (lock) {
            UpdateOrderItemCountService updateOrderItemCountService = BeanUtil.getBean(UpdateOrderItemCountService.class);
            List<Stock> stockListResult = updateOrderItemCountService.updateOrderItemCount(orderInDb, orderItem, countAfterUpdate, orderItemId);

            //库存不够了
            if (isNotEmpty(stockListResult)) {
                logger.info("stock list result is {}", StringUtils.join(stockListResult, ","));
                return stockListResult;
            }

            //库存够并且已经减掉了Item里面的锁定量，再写订单的数据
            OrderItem cloneOrderItem = (OrderItem) ClassUtil.clone(orderItem);
            cloneOrderItem.setCount(countAfterUpdate);
            updateOrderItemLockNumber(orderInDb, cloneOrderItem, true);
        }

        return new ArrayList<>();
    }


    /**
     * 菜到锁定量
     *
     * @param orderItem
     * @return
     */
    public OrderItemLockNumber orderItemToOrderItemLockNumber(OrderItem orderItem) throws Exception {
        List<ItemLockNumber> itemLockNumberList = orderItemToLockNumberList(orderItem);
        OrderItemLockNumber itemLockNumber = new OrderItemLockNumber();
        itemLockNumber.setOrderItemId(orderItem.getId());
        itemLockNumber.setItemLockNumberList(itemLockNumberList);
        return itemLockNumber;
    }

    /**
     * 菜到锁定量
     *
     * @param orderItem
     * @return
     */
    public List<ItemLockNumber> orderItemToLockNumberList(OrderItem orderItem) throws Exception {
        List<ItemNumber> itemNumberList = OrderService.getInstance().orderItemToItemNumber(orderItem);
        //依次将ItemNumber转化成OrderItemLockNumber
        return itemNumberList.stream().map(itemNumber -> itemNumberToItemLockNumber(itemNumber)).collect(Collectors.toList());
    }

    /**
     * 将菜的数据转化成ItemLockNumber
     *
     * @param itemNumber
     * @return
     */
    public ItemLockNumber itemNumberToItemLockNumber(ItemNumber itemNumber) {
        ItemLockNumber itemLockNumber = new ItemLockNumber();
        itemLockNumber.setItemId(itemNumber.getItemId());
        itemLockNumber.setLockCount(itemNumber.getNumber());
        return itemLockNumber;
    }

    /**
     * 更新菜品里面的某一个菜时，同步更新订单里面的锁定量，以及菜品里面的锁定量
     *
     * @param orderInDb
     * @param reqOrderItem
     * @return
     */
    public List<Stock> updateOrderItem(Order orderInDb, OrderItem reqOrderItem) throws Exception {
        if (!config.stockManageOpen()) {
            return new ArrayList<>();
        }

        //特别菜不处理库存
        if (equals(reqOrderItem.getItemId(), SPECIAL_ITEM)) {
            return new ArrayList<>();
        }

        //只有服务员才需要更新，不是服务员不处理
        Long userId = ServletUtil.getUserId();
        if (userId == null) {
            return new ArrayList<>();
        }

        OrderItem orderItemInDb = OrderService.getInstance().filterOrderItemList(orderInDb, reqOrderItem.getId());

        if (!isRequestChangeCount(reqOrderItem, orderItemInDb)) {
            logger.info("do not change the count {}", reqOrderItem.getCount());
            return new ArrayList<>();
        }

        //先将现有的锁定量清空，然后新处理锁定量
        {
            OrderItemLockNumber orderItemLockNumber = filterOrderItemLockNumber(orderInDb, reqOrderItem.getId());
            releaseLockNumber(orderItemLockNumber);
        }

        //添加新的锁定量
        List<Stock> stockList = orderItemToStock(reqOrderItem, false);
        List<Stock> stocks = stockManage(orderInDb.getShopId(), stockList);

        if (isNotEmpty(stocks)) {
            return stocks;
        }

        //成功锁定，添加订单里面的锁定量
        updateOrderItemLockNumber(orderInDb, reqOrderItem, true);

        return new ArrayList<>();
    }

    /**
     * 是否改变了数量了
     *
     * @param reqOrderItem
     * @param orderItemInDb
     * @return
     */
    public boolean isRequestChangeCount(OrderItem reqOrderItem, OrderItem orderItemInDb) {
        return reqOrderItem.getCount() != null && !equals(orderItemInDb.getCount(), reqOrderItem.getCount());
    }


    /**
     * 购物车里面的一个菜转化成库存管理
     * <p>
     * 均是锁定量，也就是B端操作的
     *
     * @param orderItem
     * @return
     */
    public List<Stock> orderItemToStock(OrderItem orderItem, Boolean cutLeftCount) throws Exception {
        Long itemId = orderItem.getItemId();
        Set<Stock> stockSet = new HashSet<>();
        Item item = ItemService.getInstance().findItem(itemId);
        Integer count = orderItem.getCount();

        if (count == null) {
            count = 1;
        }

        {
            //临时处理，作用域范围不扩张
            Stock stock = new Stock();
            stock.setItemId(itemId);
            if (getBoolean(cutLeftCount)) {
                stock.setLeftCount(count);
            } else {
                stock.setLockCount(count);
            }

            stockSet.add(stock);
        }

        //套餐，添加下面的菜
        if (getBoolean(item.getSetMenu())) {
            List<Item> itemList = getList(orderItem.getSetMenuOneItemList()).stream().flatMap(setMenuOneItem -> {
                return getList(setMenuOneItem.getItemList()).stream();
            }).collect(Collectors.toList());

            //多个相同item是多个对象
            for (Item itemTmp : itemList) {
                //找到了，更新
                Optional<Stock> stockOptional = stockSet.stream().filter(p -> equals(p.getItemId(), itemTmp.getId())).findAny();
                if (stockOptional.isPresent()) {
                    Stock stock = stockOptional.get();

                    if (getBoolean(cutLeftCount)) {
                        stock.setLeftCount(stock.getLeftCount() + count * OrderService.getInstance().getCountInOrder(itemTmp));
                    } else {
                        stock.setLockCount(stock.getLockCount() + count * OrderService.getInstance().getCountInOrder(itemTmp));
                    }
                } else {
                    //没有找到，新建一个
                    Stock stock = new Stock();
                    stock.setItemId(itemTmp.getId());

                    if (getBoolean(cutLeftCount)) {
                        stock.setLeftCount(count * OrderService.getInstance().getCountInOrder(itemTmp));
                    } else {
                        stock.setLockCount(count * OrderService.getInstance().getCountInOrder(itemTmp));
                    }

                    stockSet.add(stock);
                }
            }
        }

        return new ArrayList<>(stockSet);
    }

    /***
     * 更新锁定量
     *
     * @param orderInDb
     * @param orderItem
     */
    public void updateOrderItemLockNumber(Order orderInDb, OrderItem orderItem, boolean realLock) throws Exception {
        if (!config.stockManageOpen()) {
            return;
        }

        Long userId = ServletUtil.getUserId();
        if (userId == null) {
            return;
        }

        OrderItemLockNumber orderItemLockNumber = orderItemToOrderItemLockNumber(orderItem);
        orderItemLockNumber.getItemLockNumberList().forEach(itemLockNumber -> itemLockNumber.setRealLock(realLock));

        //重新写锁定量
        List<OrderItemLockNumber> itemLockNumberList = getList(orderInDb.getItemLockNumberList()).stream().filter(p -> !equals(p.getOrderItemId(), orderItem.getId())).collect(Collectors.toList());
        itemLockNumberList.add(orderItemLockNumber);
        orderInDb.setItemLockNumberList(itemLockNumberList);
    }

    /**
     * 库存列表转化成菜的列表
     *
     * @param stockList
     * @return
     */
    public List<Item> stockListToItemList(List<Stock> stockList) {
        return stockList.stream().map(stock -> {
            try {
                return ItemService.getInstance().findItem(stock.getItemId());
            } catch (Exception e) {
                logger.error("e", e);
                return null;
            }
        }).filter(item -> item != null).collect(Collectors.toList());
    }

    /**
     * 服务器在加菜的时候，处理库存,只会添加1个
     * <p>
     * ok
     *
     * @param orderInDb
     * @param reqOrderItem
     * @throws Exception
     */
    public List<Stock> waiterAddOneItem(Order orderInDb, OrderItem reqOrderItem) throws Exception {
        long startTime = System.currentTimeMillis();
        //新增的时候，需要判断是否有余量
        if (!config.stockManageOpen()) {
            logger.info("do not has stock manage");
            return new ArrayList<>();
        }

        //B端添加的时候，需要判断余量，C端添加的时候，不需要判断余量，因为C端在订单确定的时候判断
        if (ServletUtil.getUserId() != null) {
            long lockStartTime = System.currentTimeMillis();
            StockService stockService = StockService.getInstance();

            List<Stock> stockList = stockService.orderItemToStock(reqOrderItem, false);

            //如果所有的菜都没有设置余量，直接通过
            List<Item> itemList = stockListToItemList(stockList);
            boolean allNotLeftCount = itemList.stream().allMatch(item -> item.getLeftCount() == null);
            if (allNotLeftCount) {
                logger.info("do not has the left count for the order item {}, and cost time {}", reqOrderItem.getId(), System.currentTimeMillis() - lockStartTime);
                //直接添加锁定量，但是是虚拟的锁定量
                OrderItemLockNumber orderItemLockNumber = orderItemToOrderItemLockNumber(reqOrderItem);
                //设置成虚拟的锁定量
                orderItemLockNumber.getItemLockNumberList().forEach(itemLockNumber -> itemLockNumber.setRealLock(false));
                List<OrderItemLockNumber> itemLockNumberList = orderInDb.getItemLockNumberList();
                itemLockNumberList.add(orderItemLockNumber);
                return new ArrayList<>();
            }

            String lockKey = StockService.getInstance().getStockKey(orderInDb.getShopId());
            Object lock = localLockService.getLock(lockKey);
            //减少锁的范围
            synchronized (lock) {
                logger.info("start to stock manage");
                List<Stock> stockListResult = stockService.stockManage(orderInDb.getShopId(), stockList);

                if (isNotEmpty(stockListResult)) {
                    logger.info("stock list result is {}", StringUtils.join(stockListResult, ","));
                    return stockListResult;
                }

                //添加锁定量
                updateOrderItemLockNumber(orderInDb, reqOrderItem, true);
                logger.info("waiterAddOneItemDealStock cost time {}", System.currentTimeMillis() - lockStartTime);
                return new ArrayList<>();
            }
        }

        logger.info("waiterAddOneItemDealStock cost time {}", System.currentTimeMillis() - startTime);

        logger.info("c end add order item");
        return new ArrayList<>();
    }

    /**
     * 订单结束的时候，判断订单库存是否足够
     *
     * @param order
     * @return
     */
    public List<Stock> whenOrderCompleteCheck(Order order) throws Exception {
        OrderService orderService = OrderService.getInstance();
        List<OrderItem> lockedOrderItemList = orderService.filterLockedOrderItem(order);
        //判断这次的锁定量是否还有充足的余量
        List<Stock> stockList = checkOrderItemListStock(lockedOrderItemList);

        if (isNotEmpty(stockList)) {
            return stockList;
        }

        //锁定量转余量
        logger.info("available item enough");
        lockCountToPrinterCount(order, lockedOrderItemList);
        return new ArrayList<>();
    }

    /**
     * 释放掉订单里面锁定的菜的锁定量
     * <p>
     * ok
     *
     * @param orderInDb
     * @param deleteItemIdList
     * @throws Exception
     */
    public void releaseOrderItemLockCount(Order orderInDb, List<Long> deleteItemIdList) throws Exception {
        if (config.stockManageOpen()) {
            if (ServletUtil.getUserId() != null) {
                String lockKey = StockService.getInstance().getStockKey(orderInDb.getShopId());
                Object lock = localLockService.getLock(lockKey);

                synchronized (lock) {
                    releaseLockNumber(orderInDb, deleteItemIdList);
                }
            }
        }
    }

    /**
     * 确认订单的时候，锁定量处理
     * <p>
     * ok
     *
     * @param orderInDb
     * @param orderItemIdList
     * @return
     * @throws Exception
     */
    public List<Stock> sendToKitchen(Order orderInDb, List<Long> orderItemIdList) throws Exception {
        if (!config.stockManageOpen()) {
            return new ArrayList<>();
        }

        Long shopId = orderInDb.getShopId();
        OrderService orderService = OrderService.getInstance();

        String stockLockKey = StockService.getInstance().getStockKey(shopId);
        Object stockLock = localLockService.getLock(stockLockKey);
        synchronized (stockLock) {
            //判断这次的锁定量是否还有充足的余量
            List<Stock> stocks = StockService.getInstance().checkLockStockAvailable(orderItemIdList, orderInDb);
            StockService.getInstance().formatAvailableCount(stocks);

            //有菜品售罄了
            if (isNotEmpty(stocks)) {
                logger.info("has some item sale out or left count not enough");
                return stocks;
            }

            logger.info("modify left count ok");
            //修改余量
            StockService.getInstance().lockCountToPrinterCount(orderInDb, orderService.filterOrderItemList(orderInDb, orderItemIdList));
            //添加订单里面的打印量
            for (Long orderItemId : orderItemIdList) {
                OrderItemLockNumber orderItemLockNumber = filterOrderItemLockNumber(orderInDb, orderItemId);
                orderItemLockNumber.getItemLockNumberList().forEach(itemLockNumber -> itemLockNumber.setPrinterCount(itemLockNumber.getLockCount()));
            }

            return new ArrayList<>();
        }
    }

    /**
     * 菜转为库存管理对象
     *
     * @param orderInDb
     * @param orderIdList
     * @return
     */
    public List<Stock> orderItemLitToStock(Order orderInDb, List<Long> orderIdList, boolean willSendToKitchen) throws Exception {

        List<OrderItem> orderItemList = OrderService.getInstance().filterOrderItemList(orderInDb, orderIdList);

        Set<Stock> stockSet = new HashSet<Stock>();

        for (OrderItem orderItem : orderItemList) {
            List<Stock> stockList = orderItemToStock(orderItem, willSendToKitchen);
            for (Stock stock : stockList) {
                Optional<Stock> stockOptional = stockSet.stream().filter(p -> equals(p.getItemId(), stock.getItemId())).findAny();
                if (!stockOptional.isPresent()) {
                    stockSet.add(stock);
                    continue;
                } else {
                    Stock stockInSet = stockOptional.get();
                    //累加相同的锁定量
                    if (stockInSet.getLockCount() != null) {
                        stockInSet.setLockCount(stockInSet.getLockCount() + stock.getLockCount());
                    } else {
                        stockInSet.setLeftCount(stockInSet.getLeftCount() + stock.getLeftCount());
                    }
                }
            }
        }

        return new ArrayList<>(stockSet);
    }

    /**
     * 确认订单
     * <p>
     * ok
     *
     * @param orderInDb
     * @return
     */
    public List<Stock> confirmOrder(Order orderInDb, List<Long> orderItemIdList, boolean willSendToKitchen) throws Exception {
        if (!config.stockManageOpen()) {
            return new ArrayList<>();
        }

        logger.info("start to confirm order lock count");
        String lockKey = StockService.getInstance().getStockKey(orderInDb.getShopId());
        Object lock = localLockService.getLock(lockKey);
        synchronized (lock) {
            List<Stock> stockList = orderItemLitToStock(orderInDb, orderItemIdList, willSendToKitchen);

            //有菜品要确认，并且可用量不够
            if (isNotEmpty(stockList)) {
                //需要判断锁定量
                List<Stock> stockManageList = StockService.getInstance().stockManage(orderInDb.getShopId(), stockList);
                //不为空，说明有订单余量不足
                if (isNotEmpty(stockManageList)) {
                    return stockManageList;
                }

                //锁定量OK之后，那么需要添加锁定时间
                orderInDb.setLockTime(System.currentTimeMillis());
                orderInDb.setConfirmTime(System.currentTimeMillis());

                OrderService orderService = OrderService.getInstance();
                //添加订单的锁定量，或者打印量
                List<OrderItemLockNumber> orderItemLockNumberList = orderItemIdList.stream().map(orderItemId -> {
                    try {
                        OrderItem orderItem = orderService.filterOrderItemList(orderInDb, orderItemId);
                        OrderItemLockNumber orderItemLockNumber = orderItemToOrderItemLockNumber(orderItem);
                        if (willSendToKitchen) {
                            orderItemLockNumber.getItemLockNumberList().forEach(itemLockNumber -> itemLockNumber.setPrinterCount(itemLockNumber.getLockCount()));
                        }

                        orderItemLockNumber.getItemLockNumberList().forEach(itemLockNumber -> itemLockNumber.setRealLock(true));

                        return orderItemLockNumber;
                    } catch (Exception e) {
                        logger.error("e", e);
                        return null;
                    }
                }).filter(orderItemLockNumber -> orderItemLockNumber != null).collect(Collectors.toList());

                List<OrderItemLockNumber> orderItemLockNumberListInDb = getList(orderInDb.getItemLockNumberList());
                orderItemLockNumberListInDb.addAll(orderItemLockNumberList);
                orderInDb.setItemLockNumberList(orderItemLockNumberListInDb);
                CommonService.getInstance().save(orderInDb);
            }

            return new ArrayList<>();
        }
    }

    /**
     * 删除订单的时候，退回锁定量
     * ok
     *
     * @param orderInDb
     */
    public void deleteOrder(Order orderInDb) throws Exception {
        if (!config.stockManageOpen()) {
            return;
        }

        //依次将锁定量释放
        List<OrderItem> orderItemList = getList(orderInDb.getOrderItemDetailList()).stream().filter(p -> getBoolean(p.getLockItem())).collect(Collectors.toList());


        for (OrderItem orderItem : orderItemList) {
            //依次释放
            releaseLockNumber(orderInDb, orderItem.getId());
        }
    }

    /**
     * 过滤出选中的菜的锁定量
     *
     * @param orderInDb
     * @param orderItemId
     * @return
     */
    public OrderItemLockNumber filterOrderItemLockNumber(Order orderInDb, Long orderItemId) {
        Optional<OrderItemLockNumber> itemLockNumberOptional = getList(orderInDb.getItemLockNumberList()).stream().filter(p -> equals(p.getOrderItemId(), orderItemId)).findAny();

        //如果不存在了，有可能是已经被删除了
        if (!itemLockNumberOptional.isPresent()) {
            OrderItemLockNumber orderItemLockNumber = new OrderItemLockNumber();
            orderItemLockNumber.setOrderItemId(orderItemId);
            orderItemLockNumber.setItemLockNumberList(new ArrayList<>());
            return orderItemLockNumber;
        }

        return itemLockNumberOptional.get();
    }

    /**
     * 订单支付时候的回调
     *
     * @param parentOrder
     * @return
     * @throws Exception
     */
    public boolean stockOrderCallback(Order parentOrder) throws Exception {
        Long shopId = parentOrder.getShopId();
        //校验库存
        if (config.stockManageOpen()) {
            String stockLockKey = StockService.getInstance().getStockKey(shopId);
            Object lock = localLockService.getLock(stockLockKey);
            synchronized (lock) {
                List<Stock> stocks = StockService.getInstance().whenOrderCompleteCheck(parentOrder);
                return isEmpty(stocks);
            }
        }

        return true;
    }

    /**
     * 判断这些未发送到厨房的菜是否还有足够的余量
     *
     * @param orderItemList
     * @return
     */
    public List<Stock> checkOrderItemListStock(List<OrderItem> orderItemList) throws Exception {
        orderItemList = getList(orderItemList);
        //每一个Item id有多少锁定量
        Map<Long, Integer> itemLockMap = new HashMap<>();

        //依次判断每个菜，以及对应的锁定量是多少
        for (OrderItem orderItem : orderItemList) {
            Long itemId = orderItem.getItemId();
            logger.info("check order item id {}", itemId);
            Integer count = orderItem.getCount();
            addItemLock(itemLockMap, itemId, count);

            if (getBoolean(orderItem.getSetMenu())) {
                List<SetMenuOneItem> setMenuOneItemList = getList(orderItem.getSetMenuOneItemList());
                for (SetMenuOneItem setMenuOneItem : setMenuOneItemList) {
                    List<Item> itemList = getList(setMenuOneItem.getItemList()).stream().filter(item -> getLong(item.getId()) != 0l).collect(Collectors.toList());
                    for (Item item : itemList) {
                        addItemLock(itemLockMap, item.getId(), getInt(item.getCountInOrder()));
                    }
                }
            }
        }

        List<Stock> stockList = new ArrayList<>();
        //依次检查锁定量
        for (Map.Entry<Long, Integer> lockItem : itemLockMap.entrySet()) {
            Long itemId = lockItem.getKey();
            Integer count = lockItem.getValue();

            logger.info("check item {} , count {}", itemId, count);

            //不加配的选项，不用处理
            if (getLong(itemId) <= 0) {
                continue;
            }

            Item item = ItemService.getInstance().findItem(itemId);

            if (getBoolean(item.getSaleOut())) {
                Stock stock = new Stock();
                stock.setItemId(itemId);
                stock.setItem(item);
                stock.setSaleOut(true);
                stockList.add(stock);
                logger.info("item {} sale out ", item.getId());
                continue;
            }

            if (item.getLeftCount() == null) {
                continue;
            }

            //余量不够了，添加提示
            if (item.getLeftCount() < count) {
                Stock stock = new Stock();
                stock.setItemId(itemId);
                stock.setLeftCount(item.getLeftCount());
                stock.setAvailableCount(item.getLeftCount());
                stock.setItem(item);
                stockList.add(stock);
                logger.info("item {} not enough ", item.getId());
                continue;
            }
        }

        return stockList;
    }

    private void addItemLock(Map<Long, Integer> itemLockMap, Long itemId, Integer count) {
        if (itemLockMap.containsKey(itemId)) {
            Integer lockCount = itemLockMap.get(itemId);
            itemLockMap.put(itemId, lockCount + count);
        } else {
            itemLockMap.put(itemId, count);
        }
    }

    /**
     * 如果出现了小于0的情况，更正为0
     *
     * @param stockList
     */
    public void formatAvailableCount(List<Stock> stockList) {
        getList(stockList).forEach(stock -> {
            if (getInt(stock.getAvailableCount()) < 0) {
                stock.setAvailableCount(0);
            }
        });
    }

    /**
     * 检查没有锁定量的OrderItem
     *
     * @param orderInDb
     * @return
     * @throws Exception
     */
    public List<Stock> checkNonLockedOrderItem(Order orderInDb) throws Exception {
        orderInDb = (Order) ClassUtil.clone(orderInDb);
        OrderService orderService = OrderService.getInstance();
        //过滤已经落单并且已经确认的菜
        orderService.filterCheckoutOrderItem(orderInDb);

        //订单锁定了哪些orderItem
        List<Long> lockedOrderItemIdList = getList(orderInDb.getItemLockNumberList()).stream().map(itemLockNumber -> itemLockNumber.getOrderItemId()).collect(Collectors.toList());
        List<OrderItem> orderItemDetailList = orderInDb.getOrderItemDetailList();
        List<OrderItem> orderItemList = orderItemDetailList.stream().filter(orderItem -> !lockedOrderItemIdList.contains(orderItem.getId().longValue())).collect(Collectors.toList());
        //orderItem的菜id
        List<Long> itemIdList = orderItemList.stream().map(orderItem -> orderItem.getItemId()).collect(Collectors.toList());
        /***********
         * 要校验的菜
         *这里暂时没有考虑套餐下面的菜
         *********/
        List<Item> itemList = itemService.findItemByItemList(itemIdList);
        //设置了余量的菜
        List<Long> itemIdListHasLeftCount = itemList.stream().filter(item -> item.getLeftCount() != null).map(item -> item.getId()).collect(Collectors.toList());
        List<OrderItem> orderItems = orderItemList.stream().filter(orderItem -> itemIdListHasLeftCount.contains(orderItem.getItemId().longValue())).collect(Collectors.toList());
        List<Stock> stockListResult;

        for (OrderItem orderItem : orderItems) {
            List<Stock> stockList = orderItemToStock(orderItem, false);
            stockListResult = stockManage(orderInDb.getShopId(), stockList);

            //库存不够了
            if (isNotEmpty(stockListResult)) {
                logger.info("stock list result is {}", StringUtils.join(stockListResult, ","));
                return stockListResult;
            }
        }

        return new ArrayList<>();
    }

    /**
     * 转化有售卖的菜列表
     *
     * @param itemList
     * @return
     */
    public List<Stock> toSaleOutStockList(List<Item> itemList) {
        return itemList.stream().filter(item -> getBoolean(item.getSaleOut())).map(item -> {
            Stock stock = new Stock();
            stock.setItem(item);
            stock.setSaleOut(true);
            logger.info("sub sell out item {}", item.id);
            return stock;
        }).collect(Collectors.toList());
    }
}
